package com.yunlan.materia.aop;

import com.yunlan.materia.config.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @auther 罗显
 * @date 2025/8/8
 * @ApiNote
 */

@Slf4j
@Aspect
@Component
public class SqlLogAspect {

    @Pointcut("@annotation(com.yunlan.materia.aop.SqlLog)")
    public void printSqlPointcut() {}

    @Around("printSqlPointcut()")
    public Object interceptSqlExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();

        // 获取BoundSql（包含原始SQL和参数）
        Object[] args = joinPoint.getArgs();
        MappedStatement ms = ((SqlSessionFactory) SpringContextHolder.getBean(SqlSessionFactory.class))
                .getConfiguration().getMappedStatement(methodName);
        BoundSql boundSql = ms.getBoundSql(args[0]); //SpringContextHolder

        // 构建可执行SQL
        String executableSql = buildExecutableSql(boundSql);
        log.info("\n=== MyBatis SQL ===\nMethod: {}\nSQL: {}\n==================", methodName, executableSql);

        return joinPoint.proceed();
    }

    private String buildExecutableSql(BoundSql boundSql) {
        String sql = boundSql.getSql();
        Object[] params = boundSql.getParameterObject() instanceof Map ?
                extractFromParamMap((Map<?,?>) boundSql.getParameterObject()) :
                new Object[]{boundSql.getParameterObject()};

        // 替换SQL中的?为实际参数值
        for (Object param : params) {
            sql = sql.replaceFirst("\\?", formatParameter(param));
        }
        return sql;
    }

    private Object[] extractFromParamMap(Map<?,?> paramMap) {
        return paramMap.values().toArray();
    }

    private String formatParameter(Object param) {
        if (param == null) {
            return "NULL";
        }
        if (param instanceof String || param instanceof Character) {
            return "'" + param.toString().replace("'", "''") + "'";
        }
        return param.toString();
    }
}

